package org.refresh.core.intelligence;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*******************************************************************************
 * Copyright (c) 2005 Jules White. All rights reserved. This program and the
 * accompanying materials are made available under the terms of the Common
 * Public License v1.0 which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors: Jules White - initial API and implementation
 ******************************************************************************/
public class RefreshKB implements ConstraintEvaluatorProvider {

	public class UnsupportedObjectType extends RuntimeException {
		public UnsupportedObjectType(String msg) {
			super(msg);
		}
	}

	public class UnsupportedConstraintType extends RuntimeException {
		public UnsupportedConstraintType(String msg) {
			super(msg);
		}
	}

	private List<KBManager> knowledgeManagers_ = new ArrayList<KBManager>();

	private Map<String, ConstraintEvaluator> constraintEvaluators_ = new HashMap<String, ConstraintEvaluator>();

	private Map<String, QueryEvaluator> queryEvaluators_ = new HashMap<String, QueryEvaluator>();

	private List<ObjectView> objectViews_ = new ArrayList<ObjectView>();

	private List<ObjectType> objectTypes_ = new ArrayList<ObjectType>();

	private Map<Object, ObjectView> objectViewLookup_ = new HashMap<Object, ObjectView>();

	private Map<Object, List<ObjectType>> objectTypeLookup_ = new HashMap<Object, List<ObjectType>>();

	private Map<ObjectType, Map<Object, List<ExecutableQuery>>> queryLookup_ = new HashMap<ObjectType, Map<Object, List<ExecutableQuery>>>();

	private Map<ObjectType, List<Object>> objectsByType_ = new HashMap<ObjectType, List<Object>>();

	private List knowledgeBase_ = new ArrayList();

	public void add(Object obj) {

		if (knowledgeManagers_.size() > 0) {
			ObjectView view = null;
			for (ObjectView v : objectViews_) {
				if (v.supports(obj)) {
					view = v;
					break;
				}
			}

			if (view == null)
				throw new UnsupportedObjectType(
						"Could not find an ObjectView for the object " + obj);
			else
				objectViewLookup_.put(obj, view);

			for (KBManager mgr : knowledgeManagers_) {
				mgr.add(obj, view);
			}
		}

		List<ObjectType> types = findObjectTypes(obj);
		for (ObjectType type : types) {
			getObjectTypes(obj).add(type);
			getObjectsByType(type).add(obj);
		}

		knowledgeBase_.add(obj);
	}

	public List getObjectsByType(ObjectType t) {
		List objs = objectsByType_.get(t);
		if (objs == null) {
			objs = new ArrayList();
			objectsByType_.put(t, objs);
		}
		return objs;
	}

	public void remove(Object obj) {
		if (knowledgeManagers_.size() > 0) {
			ObjectView view = null;
			for (ObjectView v : objectViews_) {
				if (v.supports(obj)) {
					view = v;
					break;
				}
			}

			if (view != null) {

				for (KBManager mgr : knowledgeManagers_) {
					mgr.remove(obj, view);
				}
			}
		}

		objectViews_.remove(obj);
		objectTypes_.remove(obj);
		knowledgeBase_.remove(obj);
	}

	public void add(ObjectType type, Object relationship,
			ObjectType targettype, Constraint con) {
		QueryEvaluator eval = queryEvaluators_.get(con.getType());
		if (eval == null) {
			ConstraintEvaluator ceval = constraintEvaluators_
					.get(con.getType());
			if (ceval == null) {
				throw new UnsupportedConstraintType(
						"No ConstraintEvaluator is defined for the constraint type: "
								+ con.getType());
			} else {
				eval = new IteratorQueryEvaluator(this);
			}
		}

		List qs = getQueries(type, relationship);

		if (qs.size() > 0) {
			if (targettype != null) {
				((ExecutableTypedSourceQuery) qs.get(0)).getTypes().add(
						targettype);
			}
		}
		else{
			ExecutableTypedSourceQuery source = new ExecutableTypedSourceQuery(this);
			qs.add(source);
			if (targettype != null) {
				source.getTypes().add(targettype);
			}
		}

		ExecutableQuery eq = new ExecutableQuery(eval, con);
		qs.add(eq);
	}
	
	public List validTargets(Object obj, Object rel){
		List<ExecutableQuery> qs = collectQueries(obj, rel);
		List world = null;
		for(ExecutableQuery q : qs){
			world = q.run(obj, world);
		}
		return world;
	}

	public List<ExecutableQuery> collectQueries(Object obj, Object rel) {
		List<ObjectType> types = getObjectTypes(obj);
		if (types == null || types.size() == 0) {
			types = findObjectTypes(obj);
		}

		List<ExecutableQuery> queries = new ArrayList<ExecutableQuery>();
		for (ObjectType t : types) {
			List<ExecutableQuery> tqueries = getQueries(t, rel);
			if (tqueries != null) {
				queries.addAll(tqueries);
			}
		}

		return queries;
	}

	public List<ObjectType> findObjectTypes(Object obj) {
		ArrayList<ObjectType> result = new ArrayList<ObjectType>();
		for (ObjectType type : objectTypes_) {
			if (type.instanceOf(obj)) {
				result.add(type);
			}
		}
		return result;
	}

	public ConstraintEvaluator getConstraintEvaluator(String type) {
		return constraintEvaluators_.get(type);
	}

	public List<ObjectType> getObjectTypes(Object obj) {
		List<ObjectType> types = objectTypeLookup_.get(obj);
		if (types == null) {
			types = new ArrayList<ObjectType>();
			objectTypeLookup_.put(obj, types);
		}
		return types;
	}

	public List<ExecutableQuery> getQueries(ObjectType type, Object relation) {
		List<ExecutableQuery> queries = getQueryMap(type).get(relation);
		if (queries == null) {
			queries = new ArrayList<ExecutableQuery>();
			getQueryMap(type).put(relation, queries);
		}
		return queries;
	}

	public Map<Object, List<ExecutableQuery>> getQueryMap(ObjectType type) {
		Map<Object, List<ExecutableQuery>> map = queryLookup_.get(type);
		if (map == null) {
			map = new HashMap<Object, List<ExecutableQuery>>();
			queryLookup_.put(type, map);
		}
		return map;
	}

	/**
	 * @return the constraintEvaluators
	 */
	public Map<String, ConstraintEvaluator> getConstraintEvaluators() {
		return constraintEvaluators_;
	}

	/**
	 * @param constraintEvaluators
	 *            the constraintEvaluators to set
	 */
	public void setConstraintEvaluators(
			Map<String, ConstraintEvaluator> constraintEvaluators) {
		constraintEvaluators_ = constraintEvaluators;
	}

	/**
	 * @return the knowledgeBase
	 */
	public List getKnowledgeBase() {
		return knowledgeBase_;
	}

	/**
	 * @param knowledgeBase
	 *            the knowledgeBase to set
	 */
	public void setKnowledgeBase(List knowledgeBase) {
		knowledgeBase_ = knowledgeBase;
	}

	/**
	 * @return the knowledgeManagers
	 */
	public List<KBManager> getKnowledgeManagers() {
		return knowledgeManagers_;
	}

	/**
	 * @param knowledgeManagers
	 *            the knowledgeManagers to set
	 */
	public void setKnowledgeManagers(List<KBManager> knowledgeManagers) {
		knowledgeManagers_ = knowledgeManagers;
	}

	/**
	 * @return the objectTypeLookup
	 */
	public Map<Object, List<ObjectType>> getObjectTypeLookup() {
		return objectTypeLookup_;
	}

	/**
	 * @param objectTypeLookup
	 *            the objectTypeLookup to set
	 */
	public void setObjectTypeLookup(
			Map<Object, List<ObjectType>> objectTypeLookup) {
		objectTypeLookup_ = objectTypeLookup;
	}

	/**
	 * @return the objectTypes
	 */
	public List<ObjectType> getObjectTypes() {
		return objectTypes_;
	}

	/**
	 * @param objectTypes
	 *            the objectTypes to set
	 */
	public void setObjectTypes(List<ObjectType> objectTypes) {
		objectTypes_ = objectTypes;
	}

	/**
	 * @return the objectViewLookup
	 */
	public Map<Object, ObjectView> getObjectViewLookup() {
		return objectViewLookup_;
	}

	/**
	 * @param objectViewLookup
	 *            the objectViewLookup to set
	 */
	public void setObjectViewLookup(Map<Object, ObjectView> objectViewLookup) {
		objectViewLookup_ = objectViewLookup;
	}

	/**
	 * @return the objectViews
	 */
	public List<ObjectView> getObjectViews() {
		return objectViews_;
	}

	/**
	 * @param objectViews
	 *            the objectViews to set
	 */
	public void setObjectViews(List<ObjectView> objectViews) {
		objectViews_ = objectViews;
	}

	/**
	 * @return the queryEvaluators
	 */
	public Map<String, QueryEvaluator> getQueryEvaluators() {
		return queryEvaluators_;
	}

	/**
	 * @param queryEvaluators
	 *            the queryEvaluators to set
	 */
	public void setQueryEvaluators(Map<String, QueryEvaluator> queryEvaluators) {
		queryEvaluators_ = queryEvaluators;
	}

	/**
	 * @return the queryLookup
	 */
	public Map<ObjectType, Map<Object, List<ExecutableQuery>>> getQueryLookup() {
		return queryLookup_;
	}

	/**
	 * @param queryLookup
	 *            the queryLookup to set
	 */
	public void setQueryLookup(
			Map<ObjectType, Map<Object, List<ExecutableQuery>>> queryLookup) {
		queryLookup_ = queryLookup;
	}

}
